package github.sf.fw.tools.db.sql;

import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
import com.alibaba.druid.sql.builder.SQLBuilder;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleInsertStatement;
import com.alibaba.druid.sql.dialect.postgresql.ast.stmt.PGInsertStatement;

import java.util.Map;
import java.util.Set;

/**
 * 构建Insert语句
 *
 * @author zhoup
 * @since 2023/4/21
 */
public class SQLInsertBuilder implements SQLBuilder {
    private final DbType dbType;
    private SQLInsertStatement stmt;
    private boolean addColumn = false;

    public SQLInsertBuilder() {
        this(DbType.mysql);
    }

    public SQLInsertBuilder(DbType dbType) {
        this.dbType = dbType;
    }

    public SQLInsertBuilder from(String tableName) {
        SQLInsertStatement insertStatement = getSqlInsertStatement();
        SQLIdentifierExpr sqlIdentifierExpr = new SQLIdentifierExpr(tableName);
        insertStatement.setTableSource(sqlIdentifierExpr);
        return this;
    }

    public SQLInsertBuilder value(Map<String, Object> insertData) {
        SQLInsertStatement sqlInsertStatement1 = getSqlInsertStatement();
        if (!addColumn) {
            Set<String> strings = insertData.keySet();
            for (String column : strings) {
                sqlInsertStatement1.addColumn(new SQLCharExpr(column));
            }
            this.addColumn = true;
        }
        SQLInsertStatement.ValuesClause valuesClause = new SQLInsertStatement.ValuesClause();
        for (Map.Entry<String, Object> entry : insertData.entrySet()) {
            valuesClause.addValue(entry.getValue());
        }
        sqlInsertStatement1.addValueCause(valuesClause);
        return this;
    }

    public String getSQL() {
        return this.toString();
    }

    @Override
    public String toString() {
        return SQLUtils.toSQLString(stmt, dbType);
    }

    private SQLInsertStatement getSqlInsertStatement() {
        if (stmt == null) {
            this.stmt = createInsertStatement();
            return stmt;
        }
        return stmt;
    }

    private SQLInsertStatement createInsertStatement() {
        switch (dbType) {
            case oracle:
                return new OracleInsertStatement();
            case mysql:
            case mariadb:
            case tidb:
                return new MySqlInsertStatement();
            case postgresql:
                return new PGInsertStatement();
            default:
                return new SQLInsertStatement();
        }
    }
}
